package com.xneure.database.dao;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.jdbc.core.JdbcTemplate;

import com.xneure.database.annotation.DBField;
import com.xneure.database.metaobject.FieldMetaObject;
import com.xneure.database.sql.QueryMappingParser;
import com.xneure.database.sql.handler.SqlHandler;
import com.xneure.utils.ObjectUtil;
import com.xneure.utils.SpringUtil;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class EntityProxy implements MethodInterceptor {
	private Map<String, FieldMetaObject> proxyMap;
	private static ThreadLocal<JdbcTemplate> template = new ThreadLocal<JdbcTemplate>() {
		@Override
		protected JdbcTemplate initialValue() {
			return SpringUtil.getJdbcTemplate();
		}
	};
	private List<String> optimize;

	public EntityProxy() {

	}

	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		Object res = proxy.invokeSuper(obj, args);
		if (null == res) {
			String name = method.getName().toLowerCase();
			char charName = name.charAt(0);
			if (charName == 'g') {
				name = name.substring(3);
			} else if (charName == 'i') {
				name = name.substring(2);
			} else {
				return null;
			}
			if (optimize == null) {
				optimize = new ArrayList<>();
			}
			if (optimize.contains(name)) {
				return res;
			}
			if (null != proxyMap) {
				FieldMetaObject fmo = proxyMap.get(name);
				if (null != fmo) {
					res = loadObject(fmo, obj);
					if (null != res) {
						ObjectUtil.modifyFieldValue(obj, fmo.getField(), res);
					}
					optimize.add(name);
				}
			}
		}
		return res;
	}

	public void addMonitor(String name, FieldMetaObject fmo) {
		if (null == proxyMap) {
			proxyMap = new HashMap<>();
		}
		proxyMap.put(name, fmo);
	}

	public Object loadObject(FieldMetaObject fmo, Object obj) {
		Object res = null;
		QueryMappingParser qmp = new QueryMappingParser();
		StringBuilder where = new StringBuilder();
		DBField annotation = fmo.getAnnotation();
		where.append(annotation.where());
		where.append(annotation.value());
		where.append(" = ");
		if (obj instanceof DataBaseTable) {
			Object _value = ((DataBaseTable) obj).source.get(annotation.relation().toLowerCase());
			if (null == _value) {
				return null;
			} else {
				where.append(SqlHandler.getValueConvert().sqlValue(_value));
			}
		} else {
			where.append(SqlHandler.getValueConvert().sqlValue(ObjectUtil.getFieldValue(obj, annotation.relation())));
		}
		Class<?> targetType = qmp.getTargetType(fmo.getField(), annotation);
		List<Map<String, Object>> queryList;
		if (annotation.sql().isEmpty()) {
			queryList = template.get().queryForList(qmp.qurey(ObjectUtil.newInstance(targetType), where.toString()));
		} else {
			queryList = template.get().queryForList(annotation.sql().concat(" WHERE ".concat(where.toString())));
		}
		if (ObjectUtil.isNull(queryList) == false) {
			if (List.class.isAssignableFrom(fmo.getType())) {
				if (null != queryList) {
					List<Object> resObject = new ArrayList<>(queryList.size());
					for (Map<String, Object> map : queryList) {
						resObject.add(qmp.toObject(targetType, map));
					}
					res = resObject;
				}
			} else {
				res = qmp.toObject(targetType, queryList.get(0));
			}
		}
		if (null == res) {
			if (List.class.isAssignableFrom(fmo.getType())) {
				res = new ArrayList<Object>();
			}
		}
		return res;
	}
}
